// Released in the Public Domain. http://creativecommons.org/licenses/publicdomain/
package org.xi8ix.async.spi;

import org.xi8ix.async.AsyncEngine;
import org.xi8ix.async.Receiver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.UUID;

/**
 * @author Iain Shigeoka
 */
class SocketServer extends AbstractServer {

    private ServerSocketChannel server;

    public SocketServer(InetSocketAddress localAddress, Receiver receiver, AsyncEngine engine) throws IOException {
        super(localAddress, receiver, engine);

        server = ServerSocketChannel.open();
        server.socket().bind(localAddress);
        server.configureBlocking(false);
        engine.add(server, SelectionKey.OP_ACCEPT, acceptCallback);
    }

    public void close() throws IOException {
        engine.remove(server);
        server.socket().close(); // closes channel with it
        closed = true;
    }

    public Future<Boolean> send(InetSocketAddress dst, ByteBuffer data) {
        AbstractPeer peer = peers.get(dst);
        if (peer != null) {
            return peer.send(dst,data);
        }
        return failedFuture;
    }

    private Future<Boolean> failedFuture = new Future<Boolean>(){
        public boolean cancel(boolean mayInterruptIfRunning) {
            return false;
        }

        public boolean isCancelled() {
            return false;
        }

        public boolean isDone() {
            return true;
        }

        public Boolean get() throws InterruptedException, ExecutionException {
            return false;
        }

        public Boolean get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            return false;
        }
    };

    private Runnable acceptCallback = new Runnable() {
        public void run() {
            try {
                SocketChannel channel = server.accept();
                if (channel != null) {
                    InetSocketAddress remoteAddress = (InetSocketAddress) channel.socket().getRemoteSocketAddress();
                    SocketPeer peer = new SocketPeer(channel, receiver, engine);
                    peers.put(remoteAddress, peer);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    public void cancel(UUID id) {
    }
}
